package com.qwt.part_time_api.config;

import com.qwt.part_time_api.common.provider.CustomAuthenticationProvider;
import com.qwt.part_time_api.common.provider.LocalAuthenticationEntryPoint;
import com.qwt.part_time_api.config.interceptor.UrlAbstractSecurityInterceptor;
import com.qwt.part_time_api.config.interceptor.UrlAccessDecisionManager;
import com.qwt.part_time_api.config.interceptor.UrlFilterInvocationSecurityMetadataSource;
import com.qwt.part_time_api.filter.JwtAuthenticationFilter;
import com.qwt.part_time_api.handler.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.logout.LogoutFilter;


/**
 * @author 君子慎独
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;
    /**
     * 登录成功的处理器
     */
    @Autowired
    private LocalAuthenticationSuccessHandler localAuthenticationSuccessHandler;
    /**
     * 登录失败的处理器
     */
    @Autowired
    private LocalAuthenticationFailureHandler localAuthenticationFailureHandler;
    /**
     * 匿名访问无权访问资源的处理器
     */
    @Autowired
    private LocalAuthenticationEntryPoint localAuthenticationEntryPoint;
    /**
     * 权限不足处理器
     */
    @Autowired
    private LocalAccessDeniedHandler localAccessDeniedHandler;

    /**
     * 注销成功处理器
     */
    @Autowired
    private LocalLogoutSuccessHandler logoutSuccessHandler;

    /**
     * 获取访问url所需要的角色信息
     */
    @Autowired
    private UrlFilterInvocationSecurityMetadataSource urlFilterInvocationSecurityMetadataSource;
    /**
     * 认证权限处理，将之前所获的角色权限与当前登录用户的角色对比，若包含其中一个即可正常访问
     */
    @Autowired
    private UrlAccessDecisionManager urlAccessDecisionManager;

    @Autowired
    private UrlAbstractSecurityInterceptor urlAbstractSecurityInterceptor;

    /**
     * 自定义密码校验
     */
    @Autowired
    private CustomAuthenticationProvider customAuthenticationProvider;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * jwt过滤器
     */
    @Bean
    JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {
        return new JwtAuthenticationFilter(authenticationManager());
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //自定义数据库登录逻辑和密码校验
        auth.authenticationProvider(customAuthenticationProvider)
                .userDetailsService(userDetailsService)
                //设置加密方式
                .passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        //添加自定义过滤器
        http.addFilter(jwtAuthenticationFilter())
                //权限拦截在安全拦截之前
                .addFilterBefore(urlAbstractSecurityInterceptor, FilterSecurityInterceptor.class)
                // 设置jwt过滤器在logout过滤器之前执行，不然会获取不到上下文，因为还没存进去 ：https://www.jb51.net/article/225431.htm
                .addFilterBefore(jwtAuthenticationFilter(), LogoutFilter.class);

        //关闭CSRF跨域
        http.csrf().disable();

        //前后端分离是无状态的，所以使用STATELESS策略
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        //授权
        http.authorizeRequests()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                        //决策管理器
                        o.setAccessDecisionManager(urlAccessDecisionManager);
                        //安全元数据源
                        o.setSecurityMetadataSource(urlFilterInvocationSecurityMetadataSource);
                        return o;
                    }
                })
                //放行不需要拦截的请求
                .antMatchers("/login",
                        "/logout",
                        "/task/**",
                        "area/**",
                        "merchants/**",
                        "news/**",
                        "species/**",
                        "type/**",
                        "user/**")
                .permitAll()
                //所有请求必须认证才能访问
                .anyRequest().authenticated()

                //注销
                .and()
                .logout()
                .permitAll()
                //注销成功
                .logoutSuccessHandler(logoutSuccessHandler)
                //使会话失效
                .invalidateHttpSession(true)
                //注销后，删除cookie
                .deleteCookies("JSESSIONID")
                // 清除身份验证
                .clearAuthentication(true)

                .and()
                //设置登录方式为表单提交
                .formLogin()
                .permitAll()
                .successHandler(localAuthenticationSuccessHandler)
                .failureHandler(localAuthenticationFailureHandler)

                .and()
                .exceptionHandling()
                //权限不足处理
                .accessDeniedHandler(localAccessDeniedHandler)
                //未登录，访问资源的异常
                .authenticationEntryPoint(localAuthenticationEntryPoint);
    }
}

